Add the config file parsing for the on_{poweroff,reboot,crash} options, so that
authoremellor@ewan <emellor@ewan>
Fri, 30 Sep 2005 16:10:20 +0000 (17:10 +0100)
committeremellor@ewan <emellor@ewan>
Fri, 30 Sep 2005 16:10:20 +0000 (17:10 +0100)
they actually take effect.  Added behaviour "rename-restart" for debugging
purposes, that renames the domain out of the way, preserving it for debugging,
but starts a new domain too.

Add explicit remove of old domain paths when creating a new domain, to avoid
stale information affecting us (by shutting the domain down, for example).

Signed-off-by: Ewan Mellor <ewan@xensource.com>
tools/examples/xmexample.vmx
tools/examples/xmexample1
tools/examples/xmexample2
tools/examples/xmexample3
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xm/create.py

index 9b1ca8f221dd809921fa9afa66b5efd49628f1cb..89f934ddd915341e85ba216e5391d5f9f8a4c939 100644 (file)
@@ -50,10 +50,15 @@ disk = [ 'file:/var/images/min-el3-i386.img,ioemu:hda,w' ]
 #----------------------------------------------------------------------------
 # Configure the behaviour when a domain exits.  There are three 'reasons'
 # for a domain to stop: poweroff, reboot, and crash.  For each of these you
-# may specify "destroy", meaning that the domain is cleaned up as normal,
-# "restart", meaning that a new domain is started in place of the old one, or
-# "preserve", meaning that no clean-up is done until the domain is manually
-# destroyed (using xm destroy, for example).
+# may specify:
+#
+#   "destroy",        meaning that the domain is cleaned up as normal;
+#   "restart",        meaning that a new domain is started in place of the old
+#                     one;
+#   "preserve",       meaning that no clean-up is done until the domain is
+#                     manually destroyed (using xm destroy, for example); or
+#   "rename-restart", meaning that the old domain is not cleaned up, but is
+#                     renamed and a new domain started in its place.
 #
 # The default is
 #
index bd2e70d9a2eec49746fdfb21a743607d235b5d2c..be3af30a952466201955a9e527c1c111916a0620 100644 (file)
@@ -93,10 +93,15 @@ extra = "4"
 #----------------------------------------------------------------------------
 # Configure the behaviour when a domain exits.  There are three 'reasons'
 # for a domain to stop: poweroff, reboot, and crash.  For each of these you
-# may specify "destroy", meaning that the domain is cleaned up as normal,
-# "restart", meaning that a new domain is started in place of the old one, or
-# "preserve", meaning that no clean-up is done until the domain is manually
-# destroyed (using xm destroy, for example).
+# may specify:
+#
+#   "destroy",        meaning that the domain is cleaned up as normal;
+#   "restart",        meaning that a new domain is started in place of the old
+#                     one;
+#   "preserve",       meaning that no clean-up is done until the domain is
+#                     manually destroyed (using xm destroy, for example); or
+#   "rename-restart", meaning that the old domain is not cleaned up, but is
+#                     renamed and a new domain started in its place.
 #
 # The default is
 #
index fb7bd05fc567241c7951a4e3bff455d5d4d6b225..8272e84ab504daadc22d84ed458ebb1c1f36b9fd 100644 (file)
@@ -129,10 +129,15 @@ extra = "4 VMID=%d usr=/dev/sda6" % vmid
 #----------------------------------------------------------------------------
 # Configure the behaviour when a domain exits.  There are three 'reasons'
 # for a domain to stop: poweroff, reboot, and crash.  For each of these you
-# may specify "destroy", meaning that the domain is cleaned up as normal,
-# "restart", meaning that a new domain is started in place of the old one, or
-# "preserve", meaning that no clean-up is done until the domain is manually
-# destroyed (using xm destroy, for example).
+# may specify:
+#
+#   "destroy",        meaning that the domain is cleaned up as normal;
+#   "restart",        meaning that a new domain is started in place of the old
+#                     one;
+#   "preserve",       meaning that no clean-up is done until the domain is
+#                     manually destroyed (using xm destroy, for example); or
+#   "rename-restart", meaning that the old domain is not cleaned up, but is
+#                     renamed and a new domain started in its place.
 #
 # The default is
 #
index e0e1775589cf011d3d013db6fa6e9455a65f7191..45211c71296f53021b97a0997de2012c2bd90a98 100644 (file)
@@ -126,10 +126,15 @@ extra = "4 VMID=%d" % vmid
 #----------------------------------------------------------------------------
 # Configure the behaviour when a domain exits.  There are three 'reasons'
 # for a domain to stop: poweroff, reboot, and crash.  For each of these you
-# may specify "destroy", meaning that the domain is cleaned up as normal,
-# "restart", meaning that a new domain is started in place of the old one, or
-# "preserve", meaning that no clean-up is done until the domain is manually
-# destroyed (using xm destroy, for example).
+# may specify:
+#
+#   "destroy",        meaning that the domain is cleaned up as normal;
+#   "restart",        meaning that a new domain is started in place of the old
+#                     one;
+#   "preserve",       meaning that no clean-up is done until the domain is
+#                     manually destroyed (using xm destroy, for example); or
+#   "rename-restart", meaning that the old domain is not cleaned up, but is
+#                     renamed and a new domain started in its place.
 #
 # The default is
 #
index dac0affce61e8f3fa24e1a9609ccc79e2366128f..5a4d145a6fcac7f040c275b9eb12cf08346052b8 100644 (file)
@@ -70,7 +70,8 @@ shutdown_reasons = {
 restart_modes = [
     "restart",
     "destroy",
-    "preserve"
+    "preserve",
+    "rename-restart"
     ]
 
 STATE_VM_OK         = "ok"
@@ -653,21 +654,26 @@ class XendDomainInfo:
                 restart_reason = 'crash'
 
             elif xeninfo['shutdown']:
-                reason = shutdown_reason(xeninfo['shutdown_reason'])
+                if self.readDom('xend/shutdown'):
+                    # We've seen this shutdown already, but we are preserving
+                    # the domain for debugging.  Leave it alone.
+                    pass
+                else:
+                    reason = shutdown_reason(xeninfo['shutdown_reason'])
 
-                log.info('Domain has shutdown: name=%s id=%d reason=%s.',
-                         self.info['name'], self.domid, reason)
+                    log.info('Domain has shutdown: name=%s id=%d reason=%s.',
+                             self.info['name'], self.domid, reason)
 
-                self.clearRestart()
+                    self.clearRestart()
 
-                if reason == 'suspend':
-                    self.state_set(STATE_VM_SUSPENDED)
-                    # Don't destroy the domain.  XendCheckpoint will do this
-                    # once it has finished.
-                elif reason in ['poweroff', 'reboot']:
-                    restart_reason = reason
-                else:
-                    self.destroy()
+                    if reason == 'suspend':
+                        self.state_set(STATE_VM_SUSPENDED)
+                        # Don't destroy the domain.  XendCheckpoint will do
+                        # this once it has finished.
+                    elif reason in ['poweroff', 'reboot']:
+                        restart_reason = reason
+                    else:
+                        self.destroy()
 
             else:
                 # Domain is alive.  If we are shutting it down, then check
@@ -702,6 +708,8 @@ class XendDomainInfo:
             self.storeDom('xend/shutdown_start_time', time.time())
 
 
+    ## private:
+
     def clearRestart(self):
         self.removeDom("xend/shutdown_start_time")
 
@@ -709,9 +717,10 @@ class XendDomainInfo:
     def maybeRestart(self, reason):
         # Dispatch to the correct method based upon the configured on_{reason}
         # behaviour.
-        {"destroy"  : self.destroy,
-         "restart"  : self.restart,
-         "preserve" : self.preserve}[self.info['on_' + reason]]()
+        {"destroy"        : self.destroy,
+         "restart"        : self.restart,
+         "preserve"       : self.preserve,
+         "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
 
 
     def preserve(self):
@@ -719,6 +728,10 @@ class XendDomainInfo:
                  self.domid)
 
 
+    def renameRestart(self):
+        self.restart(True)
+
+
     def dumpCore(self):
         """Create a core dump for this domain.  Nothrow guarantee."""
         
@@ -758,6 +771,8 @@ class XendDomainInfo:
         self.console_channel = None
 
 
+    ## public:
+
     def setConsoleRef(self, ref):
         self.console_mfn = ref
         self.storeDom("console/ring-ref", ref)
@@ -973,6 +988,10 @@ class XendDomainInfo:
         try:
             self.dompath = DOMROOT + str(self.domid)
 
+            # Ensure that the domain entry is clean.  This prevents a stale
+            # shutdown_start_time from killing the domain, for example.
+            self.removeDom()
+
             self.initDomain()
             self.construct_image()
             self.configure()
@@ -1083,6 +1102,8 @@ class XendDomainInfo:
             log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
 
 
+    ## private:
+
     def is_terminated(self):
         """Check if a domain has been terminated.
         """
@@ -1142,7 +1163,7 @@ class XendDomainInfo:
                     log.error("Recovering from above exception.")
         self.storeDom(path, ret.port1)
         return ret
-        
+
     def create_channel(self):
         """Create the channels to the domain.
         """
@@ -1163,6 +1184,9 @@ class XendDomainInfo:
         if self.image:
             self.image.createDeviceModel()
 
+
+    ## public:
+
     def device_create(self, dev_config):
         """Create a new device.
 
@@ -1183,21 +1207,7 @@ class XendDomainInfo:
         self.configureDevice(deviceClass, devid, dev_config)
 
 
-    def restart_needed(self, reason):
-        """Determine if the vm needs to be restarted when shutdown
-        for the given reason.
-
-        @param reason: shutdown reason
-        @return True if needs restart, False otherwise
-        """
-        if self.info['restart_mode'] == RESTART_NEVER:
-            return False
-        if self.info['restart_mode'] == RESTART_ALWAYS:
-            return True
-        if self.info['restart_mode'] == RESTART_ONREBOOT:
-            return reason == 'reboot'
-        return False
-
+    ## private:
 
     def restart_check(self):
         """Check if domain restart is OK.
@@ -1216,15 +1226,17 @@ class XendDomainInfo:
         self.restart_count += 1
 
 
-    def restart(self):
-        """Restart the domain after it has exited. """
+    def restart(self, rename = False):
+        """Restart the domain after it has exited.
+
+        @param rename True if the old domain is to be renamed and preserved,
+        False if it is to be destroyed.
+        """
 
         #            self.restart_check()
 
         config = self.sxpr()
 
-        self.cleanupDomain()
-
         if self.readVm('xend/restart_in_progress'):
             log.error('Xend failed during restart of domain %d.  '
                       'Refusing to restart to avoid loops.',
@@ -1235,7 +1247,12 @@ class XendDomainInfo:
         self.writeVm('xend/restart_in_progress', 'True')
 
         try:
-            self.destroy()
+            if rename:
+                self.preserveShutdownDomain()
+            else:
+                self.cleanupDomain()
+                self.destroy()
+                
             try:
                 xd = get_component('xen.xend.XendDomain')
                 xd.domain_unpause(xd.domain_create(config).getDomid())
@@ -1248,6 +1265,37 @@ class XendDomainInfo:
         #        self.exportToDB()
 
 
+    def preserveShutdownDomain(self):
+        """Preserve a domain that has been shut down, by giving it a new UUID,
+        cloning the VM details, and giving it a new name.  This allows us to
+        keep this domain for debugging, but restart a new one in its place
+        preserving the restart semantics (name and UUID preserved).
+        """
+        
+        new_name = self.generateShutdownName()
+        new_uuid = getUuid()
+        log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
+                 self.info['name'], self.domid, self.uuid, new_name, new_uuid)
+        self.release_devices()
+        self.info['name'] = new_name
+        self.uuid = new_uuid
+        self.vmpath = VMROOT + new_uuid
+        self.storeVmDetails()
+        self.storeDom('vm', self.vmpath)
+        self.storeDom('xend/shutdown', 'True')
+
+
+    def generateShutdownName(self):
+        n = 1
+        while True:
+            name = "%s-%d" % (self.info['name'], n)
+            try:
+                self.check_name(name)
+                return name
+            except VmError:
+                n += 1
+
+
     def configure_bootloader(self):
         if not self.info['bootloader']:
             return
index a5998900534f6e67ba3cfc963779626ce68774a9..a6498c72add5c95b979ab10bf6d77b6521e6a81e 100644 (file)
@@ -163,11 +163,47 @@ gopts.var('cpu_weight', val='WEIGHT',
 
 gopts.var('restart', val='onreboot|always|never',
           fn=set_value, default=None,
-          use="""Whether the domain should be restarted on exit.
+          use="""Deprecated.  Use on_poweroff, on_reboot, and on_crash
+          instead.
+
+          Whether the domain should be restarted on exit.
           - onreboot: restart on exit with shutdown code reboot
           - always:   always restart on exit, ignore exit code
           - never:    never restart on exit, ignore exit code""")
 
+gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
+          fn=set_value, default=None,
+          use="""Behaviour when a domain exits with reason 'poweroff'.
+          - destroy:        the domain is cleaned up as normal;
+          - restart:        a new domain is started in place of the old one;
+          - preserve:       no clean-up is done until the domain is manually
+                            destroyed (using xm destroy, for example);
+          - rename-restart: the old domain is not cleaned up, but is
+                            renamed and a new domain started in its place.
+          """)
+
+gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
+          fn=set_value, default=None,
+          use="""Behaviour when a domain exits with reason 'reboot'.
+          - destroy:        the domain is cleaned up as normal;
+          - restart:        a new domain is started in place of the old one;
+          - preserve:       no clean-up is done until the domain is manually
+                            destroyed (using xm destroy, for example);
+          - rename-restart: the old domain is not cleaned up, but is
+                            renamed and a new domain started in its place.
+          """)
+
+gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
+          fn=set_value, default=None,
+          use="""Behaviour  when a domain exits with reason 'crash'.
+          - destroy:        the domain is cleaned up as normal;
+          - restart:        a new domain is started in place of the old one;
+          - preserve:       no clean-up is done until the domain is manually
+                            destroyed (using xm destroy, for example);
+          - rename-restart: the old domain is not cleaned up, but is
+                            renamed and a new domain started in its place.
+          """)
+
 gopts.var('blkif', val='no|yes',
           fn=set_bool, default=0,
           use="Make the domain a block device backend.")
@@ -536,6 +572,12 @@ def make_config(opts, vals):
         config.append(['backend', ['tpmif']])
     if vals.restart:
         config.append(['restart', vals.restart])
+    if vals.on_poweroff:
+        config.append(['on_poweroff', vals.on_poweroff])
+    if vals.on_reboot:
+        config.append(['on_reboot', vals.on_reboot])
+    if vals.on_crash:
+        config.append(['on_crash', vals.on_crash])
 
     if vals.bootloader:
         config.append(['bootloader', vals.bootloader])